/*++

Copyright (c)  1999 - 2002 Intel Corporation. All rights reserved
This software and associated documentation (if any) is furnished
under a license and may only be used or copied in accordance
with the terms of the license. Except as permitted by such
license, no part of this software or documentation may be
reproduced, stored in a retrieval system, or transmitted in any
form or by any means without the express written consent of
Intel Corporation.

Module Name:
    
    sread.c

Abstract:

    Simple read file access



Revision History

--*/

#include "lib.h"

#define SIMPLE_READ_SIGNATURE       EFI_SIGNATURE_32('s','r','d','r')
typedef struct _SIMPLE_READ_FILE {
    UINTN               Signature;
    BOOLEAN             FreeBuffer;
    VOID                *Source;
    UINTN               SourceSize;
    EFI_FILE_HANDLE     FileHandle;
} SIMPLE_READ_HANDLE;

       

EFI_STATUS
OpenSimpleReadFile (
    IN BOOLEAN                  BootPolicy,
    IN VOID                     *SourceBuffer   OPTIONAL,
    IN UINTN                    SourceSize,
    IN OUT EFI_DEVICE_PATH      **FilePath,
    OUT EFI_HANDLE              *DeviceHandle,
    OUT SIMPLE_READ_FILE        *SimpleReadHandle
    )
/*++

Routine Description:

    Opens a file for (simple) reading.  The simple read abstraction
    will access the file either from a memory copy, from a file
    system interface, or from the load file interface. 

Arguments:

Returns:

    A handle to access the file

--*/
{
    SIMPLE_READ_HANDLE          *FHand;
    EFI_DEVICE_PATH             *UserFilePath;
    EFI_DEVICE_PATH             *TempFilePath;
    EFI_DEVICE_PATH             *TempFilePathPtr;
    FILEPATH_DEVICE_PATH        *FilePathNode;
    EFI_FILE_HANDLE             FileHandle, LastHandle;
    EFI_STATUS                  Status;
    EFI_LOAD_FILE_INTERFACE     *LoadFile;
  
    FHand = NULL;
    UserFilePath = *FilePath;

    //
    // Allocate a new simple read handle structure
    //

    FHand = AllocateZeroPool (sizeof(SIMPLE_READ_HANDLE));
    if (!FHand) {
        Status = EFI_OUT_OF_RESOURCES;
        goto Done;
    }

    *SimpleReadHandle = (SIMPLE_READ_FILE) FHand;
    FHand->Signature = SIMPLE_READ_SIGNATURE;

    //
    // If the caller passed a copy of the file, then just use it
    //

    if (SourceBuffer) {
        FHand->Source = SourceBuffer;
        FHand->SourceSize = SourceSize;
        *DeviceHandle = NULL;
    if (SourceSize > 0) {
      Status = EFI_SUCCESS;
    } else {
      Status = EFI_LOAD_ERROR;
    }


      goto Done;
    } 

    //
    // Attempt to access the file via a file system interface
    //

    FileHandle = NULL;
    Status = BS->LocateDevicePath (&FileSystemProtocol, FilePath, DeviceHandle);
    if (!EFI_ERROR(Status)) {
        FileHandle = LibOpenRoot (*DeviceHandle);
    }

    Status = FileHandle ? EFI_SUCCESS : EFI_UNSUPPORTED;

    //
    // To access as a filesystem, the filepath should only
    // contain filepath components.  Follow the filepath nodes
    // and find the target file
    //

    FilePathNode = (FILEPATH_DEVICE_PATH *) *FilePath;
    while (!IsDevicePathEnd(&FilePathNode->Header)) {

        //
        // For filesystem access each node should be a filepath component
        //

        if (DevicePathType(&FilePathNode->Header) != MEDIA_DEVICE_PATH ||
            DevicePathSubType(&FilePathNode->Header) != MEDIA_FILEPATH_DP) {
            Status = EFI_UNSUPPORTED;
        }

        //
        // If there's been an error, stop
        //

        if (EFI_ERROR(Status)) {
            break;
        }
        
        //
        // Open this file path node
        //

        LastHandle = FileHandle;
        FileHandle = NULL;

        Status = LastHandle->Open (
                        LastHandle,
                        &FileHandle,
                        FilePathNode->PathName,
                        EFI_FILE_MODE_READ,
                        0
                        );
        
        //
        // Close the last node
        //
        
        LastHandle->Close (LastHandle);

        //
        // Get the next node
        //

        FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode(&FilePathNode->Header);
    }

    //
    // If success, return the FHand
    //

    if (!EFI_ERROR(Status)) {
        ASSERT(FileHandle);
        FHand->FileHandle = FileHandle;
        goto Done;
    }

    //
    // Cleanup from filesystem access
    //

    if (FileHandle) {
        FileHandle->Close (FileHandle);
        FileHandle = NULL;
        *FilePath = UserFilePath;
    }

    //
    // If the error is something other then unsupported, return it
    //

    if (Status != EFI_UNSUPPORTED) {
        goto Done;
    }

    //
    // Attempt to access the file via the load file protocol
    //

    Status = LibDevicePathToInterface (&LoadFileProtocol, *FilePath, (VOID*)&LoadFile);
    if (!EFI_ERROR(Status)) {

        TempFilePath = DuplicateDevicePath (*FilePath);

        TempFilePathPtr = TempFilePath;

        Status = BS->LocateDevicePath (&LoadFileProtocol, &TempFilePath, DeviceHandle);

        FreePool (TempFilePathPtr);

        //
        // Determine the size of buffer needed to hold the file
        //

        SourceSize = 0;
        Status = LoadFile->LoadFile (
                    LoadFile,
                    *FilePath,
                    BootPolicy,
                    &SourceSize,
                    NULL
                    );

        //
        // We expect a buffer too small error to inform us 
        // of the buffer size needed
        //

        if (Status == EFI_BUFFER_TOO_SMALL) {
            SourceBuffer = AllocatePool (SourceSize);
            
            if (SourceBuffer) {
                FHand->FreeBuffer = TRUE;
                FHand->Source = SourceBuffer;
                FHand->SourceSize = SourceSize;

                Status = LoadFile->LoadFile (
                            LoadFile,
                            *FilePath,
                            BootPolicy,
                            &SourceSize,
                            SourceBuffer
                            );  
            }
        }

        //
        // If success, return FHand
        //

        if (!EFI_ERROR(Status) || Status == EFI_ALREADY_STARTED) {
            goto Done;
        }
    }

    //
    // Nothing else to try
    //

    DEBUG ((D_LOAD|D_WARN, "OpenSimpleReadFile: Device did not support a known load protocol\n"));
    Status = EFI_NOT_FOUND;

Done:

    //
    // If the file was not accessed, clean up
    //
    if (EFI_ERROR(Status) && (Status != EFI_ALREADY_STARTED)) {
        if (FHand) {
            if (FHand->FreeBuffer) {
                FreePool (FHand->Source);
            }

            FreePool (FHand);
        }
    }

    return Status;
}

EFI_STATUS
ReadSimpleReadFile (
    IN SIMPLE_READ_FILE     UserHandle,
    IN UINTN                Offset,
    IN OUT UINTN            *ReadSize,
    OUT VOID                *Buffer
    )
{
    UINTN                   EndPos;
    SIMPLE_READ_HANDLE      *FHand;
    EFI_STATUS              Status;

    FHand = UserHandle;
    ASSERT (FHand->Signature == SIMPLE_READ_SIGNATURE);
    if (FHand->Source) {

        //
        // Move data from our local copy of the file
        //

        EndPos = Offset + *ReadSize;
        if (EndPos > FHand->SourceSize) {
            *ReadSize = FHand->SourceSize - Offset;
            if (Offset >= FHand->SourceSize) {
                *ReadSize = 0;
            }
        }

        CopyMem (Buffer, (CHAR8 *) FHand->Source + Offset, *ReadSize);
        Status = EFI_SUCCESS;

    } else {

        //
        // Read data from the file
        //

        Status = FHand->FileHandle->SetPosition (FHand->FileHandle, Offset);

        if (!EFI_ERROR(Status)) {
            Status = FHand->FileHandle->Read (FHand->FileHandle, ReadSize, Buffer);
        }
    }

    return Status;
}


VOID
CloseSimpleReadFile (
    IN SIMPLE_READ_FILE     UserHandle
    )
{
    SIMPLE_READ_HANDLE      *FHand;

    FHand = UserHandle;
    ASSERT (FHand->Signature == SIMPLE_READ_SIGNATURE);

    //
    // Free any file handle we opened
    //

    if (FHand->FileHandle) {
        FHand->FileHandle->Close (FHand->FileHandle);
    }

    //
    // If we allocated the Source buffer, free it
    //

    if (FHand->FreeBuffer) {
        FreePool (FHand->Source);
    }

    //
    // Done with this simple read file handle
    //

    FreePool (FHand);
}
